package com.techprimers.mongodb.springbootmongodbexample.resource;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.techprimers.mongodb.springbootmongodbexample.bean.DeviceStatus;
import com.techprimers.mongodb.springbootmongodbexample.document.DeviceStatusHistory;
import com.techprimers.mongodb.springbootmongodbexample.repository.DeviceStatusHistoryRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

@Slf4j
@RestController
@RequestMapping("/device/status")
public class DeviceStatusHistoryResource {

    @Autowired
    private DeviceStatusHistoryRepository deviceStatusHistoryRepository;

    @GetMapping("/history")
    public List<DeviceStatusHistory> getAll() {
        return deviceStatusHistoryRepository.findAll();
    }

    private DeviceStatusHistory buildStatus() {
        DeviceStatusHistory history = new DeviceStatusHistory();
        history.setCreateTime(new Date()).setUpdateTime(new Date()).setDeviceId(RandomUtil.randomStringUpper(3))
                .setStartTimestamp(System.currentTimeMillis())
                .setPackTimestamp(System.currentTimeMillis() / 1000);
        return history;
    }


    @GetMapping("/saveBean")
    public void saveBean() {
        log.info("Save device status history test now!");
        DeviceStatusHistory history = buildStatus();
        List<Object> statusList = new ArrayList<>();
        statusList.add(new DeviceStatus());
        statusList.add(new DeviceStatus());
        statusList.add(new DeviceStatus());
        statusList.add(new DeviceStatus());
        history.setStatusHistory(statusList);
        deviceStatusHistoryRepository.save(history);
        log.info("Save device status history test finished!");
    }

    private static final String TEST_HISTORY_DATA_FILE_NAME = "deviceStatusHistoryData.txt";

    private List<String> createStatusJson(int recordCount) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < recordCount; i++) {
            list.add(JSONUtil.toJsonStr(new DeviceStatus()));
        }
        return list;
    }

    @GetMapping("/createJsonFile")
    private void createTestJson() {
        final List<String> testJson = createStatusJson(1000);
        final String targetFilePath = getClass().getClassLoader().getResource(TEST_HISTORY_DATA_FILE_NAME).getFile();
        FileUtil.writeLines(testJson, new File(targetFilePath), Charset.defaultCharset());
    }

    @GetMapping("/saveJson")
    public void saveJson() {
        final List<String> lines = FileUtil.readLines(getClass().getClassLoader().getResource(TEST_HISTORY_DATA_FILE_NAME), Charset.defaultCharset());
        Assert.isTrue(!CollectionUtils.isEmpty(lines), "Json file is empty!");

        List<Object> list = new ArrayList<>();
        lines.forEach(line -> {
            list.add(JSONUtil.parseObj(line));
        });

        DeviceStatusHistory history = buildStatus();
        history.setStatusHistory(list);
        deviceStatusHistoryRepository.save(history);
        log.info("Save device status history json test finished!");
    }

    private BasicDBObject getBasicDBObject(final String json) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        TypeReference<HashMap<String, Object>> typeRef
                = new TypeReference<HashMap<String, Object>>() {
        };

        HashMap<String, Object> mapU1 = mapper.readValue(json, typeRef);

        return new BasicDBObject(mapU1);
    }

    @GetMapping("/saveBJson")
    public void saveAsBJson() throws IOException {
        final List<Object> histories = new ArrayList<>();
        final List<String> lines = FileUtil.readLines(getClass().getClassLoader().getResource(TEST_HISTORY_DATA_FILE_NAME), Charset.defaultCharset());
        Assert.isTrue(!CollectionUtils.isEmpty(lines), "File empty!");

        for (String line : lines) {
            log.info("json string = {}", line);
            final BasicDBObject object = getBasicDBObject(line);
            if (null != object) {
                histories.add(object);
            }
        }

        DeviceStatusHistory history = buildStatus();
        history.setStatusHistory(histories);
        deviceStatusHistoryRepository.save(history);
        log.info("Save device status history bjson test finished!");
    }

    @Autowired
    private MongoTemplate mongoTemplate;

    @GetMapping("/findByTemplate")
    public List<DeviceStatusHistory> findByTemplate() {
        BasicDBList basicDBList = new BasicDBList();
        basicDBList.add(new BasicDBObject("device_id", "A4R"));

        DBObject obj = new BasicDBObject();
        obj.put("$and", basicDBList);

        BasicDBObject fieldsObject = new BasicDBObject();
        //指定返回的字段
        fieldsObject.put("device_id", true);
        fieldsObject.put("pack_timestamp", true);
        fieldsObject.put("status_history.coordinate", true);

        Query query = new BasicQuery(obj, fieldsObject);
        return mongoTemplate.find(query, DeviceStatusHistory.class);
    }

    @GetMapping("/findByRespository")
    public List<DeviceStatusHistory> findByRespository() {
        List<DeviceStatusHistory> list = deviceStatusHistoryRepository.find("A4R");
        Assert.notNull(list, "Result is  null");
        return list;
    }

    @GetMapping("/findByDeviceIdAndNetwork")
    public void findByDeviceIdAndNetwork() {
        //封装对象列表查询条件
        List<AggregationOperation> commonOperations = new ArrayList<>();
        //1. 指定查询主文档
        MatchOperation match = Aggregation.match(Criteria.where("device_id").is("A4R"));
        commonOperations.add(match);
        //2. 指定投影，返回哪些字段
        ProjectionOperation project = Aggregation.project("_id", "device_id", "status_history");
        commonOperations.add(project);
        //3. 拆分内嵌文档
        UnwindOperation unwind = Aggregation.unwind("status_history");
        commonOperations.add(unwind);
        //4. 指定查询子文档
        MatchOperation match2 = Aggregation.match(
                Criteria.where("status_history.timestamp").is(1608467576994L));
        commonOperations.add(match2);
        //创建管道查询对象
        Aggregation aggregation = Aggregation.newAggregation(commonOperations);
        AggregationResults<JSONObject> reminds = mongoTemplate
                .aggregate(aggregation, "device_status_history", JSONObject.class);
        List<JSONObject> mappedResults = reminds.getMappedResults();
        if (mappedResults != null && mappedResults.size() > 0) {
            log.info("Result is not empty!");
        }
    }

}
